# Базы данных

## Доменное разделение

Мы не используем схему `public`.
Мы активно используем схемы для доменного разделения таблиц.
Схемы базы данных по доменному разделению должны повторять доменное разделение в самом приложении.
Иными словами, если в пакете `entity` у нас пакет `user` с классом `Person`, то и в базе данных таблица `persons` должна лежать в схеме `users`.

## Таблицы на основе внутренних ограничений

Все связи между таблицами и ссылки на них являются внутренними и генерируются базой данных.
Все ссылки между сущностями, полученные извне, являются не более чем бизнес-данными и не могут быть связями между таблицами в наших внутренних базах данных.
В силу вступает правило: если что-то зависит от чего-то, оно имеет право этим чем-то управлять.
Пример: если отдел кадров зависит от программного компонента, который, к примеру, считает отработанные часы по определённой формуле, отдел кадров может попросить эту формулу изменить по своему усмотрению.
Мы не можем управлять внешними связями.
Это значит, что мы не должны от них зависеть.
Для нас это просто бизнес-данные.

## Именования

Мы используем множества для именований там, где это возможно. `users`, `dictionaries`, `buffers` и так далее.

Если поле является ограничением (constraint), к нему применяются следующие правила именования:

| Тип ограничения   | Правило именования поля                 | Пример именования поля | Правило именования ограничения                            | Пример именования ограничения                                              |
|-------------------|-----------------------------------------|------------------------|-----------------------------------------------------------|----------------------------------------------------------------------------|
| Первичный ключ    | Имя внешней таблицы с постфиксом _`id`. | `user_id`              | Имя внешней таблицы с постфиксом `_pk`                    | `user_pk`                                                                  |
| Вторичный ключ    | Имя внешней таблицы с постфиксом _`id`. | `user_id`              | Имя внешней таблицы с постфиксом `_fk`                    | `user_fk`                                                                  |
| Простой индекс    |                                         |                        | Имя таблицы + имя поля с постфиксом `_idx`                | `user_name_idx`, `user_id_idx`, `user_name_type_idx` (для множества полей) |
| Уникальный индекс |                                         |                        | Имя таблицы + имя поля с постфиксом `_udx`                | `user_name_udx`, `user_id_udx`, `user_name_type_udx` (для множества полей) |
| Проверка (check)  |                                         |                        | Имя таблицы + имя поля + суть проверки с постфиксом `_ck` | `user_age_positive_ck` для `age` > 0                                       |

## Связи внутри таблиц

При построении связей внутри таблиц, мы используем тот же принцип, что и при построении связей между бизнес-сущностями и их сервисами.
Равно как сервис одной бизнес-сущности может работать с другой бизнес-сущностью только через её собственный сервис, так и таблица может работать с таблицей через явный внешний ключ.

Например, у нас есть сущность `Sign` (подпись), которая связана с другой сущностью - `Document` (Документ) - связью `Many-To-One`.
Документ является внешней сущностью, мы всегда при обращении с его данными запрашиваем его во внешнем сервисе.
На стороне таблицы `signs` напрашивается поле `document_external_id`, которое является идентификатором документа во внешнем сервисе.

Такой подход неверен.
Следуя описываемой практике, нам следует иметь таблицу `documents`, в которой будет поле `external_id`.
Ссылку же на таблицу следует делать через внешний ключ.

## Разделение служебных и бизнесовых полей

Мы придерживаемся чёткого разделения полей по их применению. Одно поле не может выполнять служебные и бизнесовые функции.

Разберём пример.

Существует бизнес-сущность User:

```kotlin
data class User(
    val id: UUID,
    val name: String
)
```

Хранение состояния пользователей обеспечивает таблица users:

```
create table users
(
    id         uuid primary key,
    name       text,
    created_at timestamptz,
    active     boolean
)
```

Очевидно, что поле name в таблице является бизнесовым, тогда как поля id, created_at, active являются служебными.
Эти поля отсутствуют в бизнес-сущности.

Возьмём более сложный пример.

```
create table users
(
    id         uuid primary key,
    name       text,
    created_at timestamptz,
    active     boolean,
    person_id  text
)
```

В данном примере, `person_id` является внешним идентификатором, который был создан в другой системе.
Наш сервис не контролирует эти идентификаторы и не может гарантировать, что они не изменятся.
Стало быть, для нашего сервиса они бизнесовые.

